//== SAGITTARIUS ============================================================================

//== BEGINNING OF CODE ===============================================================

#include "sagittarius/core/SagittariusCore.h"
#include "sagittarius/lexer/lexcore.h"
#include "sagittarius/io/io.h"
#include <string.h>

LexBuf sg_lex_pushsym(constLexBuf p, LXConstAlphaType x) {
    if(p == NULL) {
        LexBuf b = LX_CREATE_POOL_BUF(2);
        LX_INIT_SET(b[0], x);
        LX_INIT_SET_SPC(b[1], '\0'); //terminating char
        return b;
    } else {
        size_t len=LX_BUFFER_LEN(p);
        LexBuf b = LX_CREATE_POOL_BUF(len+1);
//         SG_WRITE(2, "Pushing onto string ");
//         LX_WRITE_BUF(2, p);
//         SG_WRITE(2, "\n");
        LX_COPY(b, p, len-1);
        LX_INIT_SET(b[len-1], x);
        LX_INIT_SET_SPC(b[len], '\0'); //terminating char
        return b;
    }
}

LexBuf sg_lex_copybuf(constLexBuf p) {
    size_t pl;
    LexBuf b;
    if(p)
        pl = LX_BUFFER_LEN(p);
    else
        return NULL;
    b = LX_CREATE_BUFFER(pl);
    LX_COPY(b, p, pl-1);
    LX_ALPHA_SET_SPC(b[pl-1], '\0');
    return b;
}

LexBuf sg_lex_copypbuf(constLexBuf p) {
    size_t pl;
    LexBuf b;
    if(p)
        pl = LX_BUFFER_LEN(p);
    else
        return NULL;
    b = LX_CREATE_POOL_BUF(pl);
    LX_COPY(b, p, pl-1);
    LX_ALPHA_SET_SPC(b[pl-1], '\0');
    return b;
}

LexBuf sg_lex_copybufl(constLexBuf p, size_t l) {
    LexBuf b;
    b = LX_CREATE_BUFFER(l+1);
    LX_COPY(b, p, l);
    LX_ALPHA_SET_SPC(b[l], '\0');
    return b;
}

LexBuf sg_lex_concatbuf(constLexBuf p, constLexBuf q) {
    size_t pl, ql;
    LexBuf b;
    if(p)
        pl = LX_BUFFER_LEN(p);
    if(q)
        ql = LX_BUFFER_LEN(q);
    if(p == NULL)
        return sg_lex_copybuf(q);
    if(q == NULL)
        return sg_lex_copybuf(p);
    b = LX_CREATE_BUFFER(pl+ql-1);
    LX_COPY(b, p, pl-1);
    LX_COPY(b+pl-1, q, ql-1);
    LX_ALPHA_SET_SPC(b[pl+ql-1], '\0');
    return b;
}

/// sg_lex_bufeq
int sg_lex_bufeq(constLexBuf p, constLexBuf q) {
    while(LX_CMPALPHA_EQ(*p, *q)) {
        if(LX_CMPALPHA_EQ_SPC(*p, '\0'))
            return 1;
        ++p;
        ++q;
    }
    return 0;
}

/// sg_lex_bufeql
int sg_lex_bufeql(constLexBuf p, constLexBuf q, size_t l) {
    while(LX_CMPALPHA_EQ(*p, *q)) {
        if(LX_CMPALPHA_EQ_SPC(*p, '\0'))
            return 0;
        ++p;
        ++q;
        --l;
    }
    if(l == 0)
        return 1;
    else
        return 0;
}

/// sg_lex_str2buf
LexBuf sg_lex_str2buf(const char* str) {
    int result;
    size_t data_size = strlen(str)+1;
    /*SG_WRITE(2, "sg_lex_str2buf: data_size = ");
    sg_writeUInt64(2, data_size);
    SG_WRITE(2, "\n");*/
    LexBuf ubuf = LX_CREATE_BUFFER(data_size);
    #if !SAGITTARIUS_LXALPHA_USE_GMP
        result = UTF8_ConvertBuffer(str, ubuf);
    #else
        result = UTF8_GMPConvertBuffer(str, ubuf);
    #endif
    if(result < 0) {
        AN(0, "Failed to convert buffer");
    }
    return ubuf;
}

/// sg_lex_buf2str
char* sg_lex_buf2str(constLexBuf b) {
    size_t encl = LX_ENCBUFFER_LEN(b);
    char* str=malloc(encl);
    LX_BUFFER_ENC_UTF8(b, str);
    return str;
}

/// sg_lex_hashbuf
uint64 sg_lex_hashbuf(constLexBuf b) {
    #if !SAGITTARIUS_LXALPHA_USE_GMP
        return sg_hash64((void*)b, (LX_BUFFER_LEN(b)-1)*sizeof(LXAlphaType));
    #else
        uint64 h;
        size_t i;
        uint64 tmp;
        h=0;
        for(i=0; i<LX_BUFFER_LEN(b)-1; ++i) {
            tmp = LX_GET_FIXED_VALUE(b[i]);
            h = h ^ sg_hash64(&tmp, sizeof(tmp));
            /*SG_WRITE(2, "sg_lex_hashbuf new key: ");
            sg_writeMemSegHex(2, &h, 8);
            SG_WRITE(2, "\n");*/
        }
        return h;
    #endif
}

uint64 sg_lex_hashbufl(constLexBuf b, size_t l) {
    #if !SAGITTARIUS_LXALPHA_USE_GMP
        return sg_hash64((void*)b, l*sizeof(LXAlphaType));
    #else
        uint64 h;
        size_t i;
        uint64 tmp;
        h=0;
        for(i=0; i<l; ++i) {
            tmp = LX_GET_FIXED_VALUE(b[i]);
            h = h ^ sg_hash64(&tmp, sizeof(tmp));
           /* SG_WRITE(2, "sg_lex_hashbufl new key: ");
            sg_writeMemSegHex(2, &h, 8);
            SG_WRITE(2, "\n");*/
        }
        return h;
    #endif
}